package visitor

import "fmt"

// Visitor 访问者接口
type Visitor interface {
	// VisitConcreteElementA 访问元素A，相当于给元素A添加访问者的功能
	VisitConcreteElementA(elementA *ConcreteElementA)

	// VisitConcreteElementB 访问元素B，相当于给元素A添加访问者的功能
	VisitConcreteElementB(elementB *ConcreteElementB)
}

// Element 被访问的元素的接口
type Element interface {
	// Accept 接受访问者的访问
	Accept(visitor Visitor)
}

// ConcreteElementA 具体元素的实现对象
type ConcreteElementA struct {
}

func (ca *ConcreteElementA) Accept(visitor Visitor) {
	// 回调访问者对象的相应方法
	visitor.VisitConcreteElementA(ca)
}

// OperationA 示例方法，表示元素已有的功能实现
func (ca *ConcreteElementA) OperationA() {
	// 已有的功能实现
	fmt.Println("ConcreteElementA", "OperationA")
}

// ConcreteElementB 具体元素的实现对象
type ConcreteElementB struct {
}

func (cb *ConcreteElementB) Accept(visitor Visitor) {
	// 回调访问者对象的相应方法
	visitor.VisitConcreteElementB(cb)
}

// OperationB 示例方法，表示元素已有的功能实现
func (cb *ConcreteElementB) OperationB() {
	// 已有的功能实现
	fmt.Println("ConcreteElementB", "OperationB")
}

// ConcreteVisitor1 具体的访问者实现
type ConcreteVisitor1 struct {
}

func (cv *ConcreteVisitor1) VisitConcreteElementA(element *ConcreteElementA) {
	// 把要访问ConcreteElementA时，需要执行的功能实现在这里
	// 可能需要访问元素已有的功能，比如：
	element.OperationA()
	fmt.Println("ConcreteVisitor1", "VisitConcreteElementA")
}

func (cv *ConcreteVisitor1) VisitConcreteElementB(element *ConcreteElementB) {
	// 把要访问ConcreteElementB时，需要执行的功能实现在这里
	// 可能需要访问元素已有的功能，比如：
	element.OperationB()
	fmt.Println("ConcreteVisitor1", "VisitConcreteElementB")
}

// ConcreteVisitor2 具体的访问者实现
type ConcreteVisitor2 struct {
}

func (cv *ConcreteVisitor2) VisitConcreteElementA(element *ConcreteElementA) {
	// 把要访问ConcreteElementA时，需要执行的功能实现在这里
	// 可能需要访问元素已有的功能，比如：
	element.OperationA()
	fmt.Println("ConcreteVisitor2", "VisitConcreteElementA")
}

func (cv *ConcreteVisitor2) VisitConcreteElementB(element *ConcreteElementB) {
	// 把要访问ConcreteElementB时，需要执行的功能实现在这里
	// 可能需要访问元素已有的功能，比如：
	element.OperationB()
	fmt.Println("ConcreteVisitor2", "VisitConcreteElementB")
}

// ObjectStruct 对象结构，通常在这里对元素对象进行遍历，让访问者能访问到所有的元素
type ObjectStruct struct {
	// 示意，表示对象结构，可以是一个组合结构或是集合
	col []Element
}

// HandleRequest 示意方法，提供给客户端操作的高层接口
func (obj *ObjectStruct) HandleRequest(visitor Visitor) {
	// 循环对象结构中的元素，接受访问
	for _, ele := range obj.col {
		ele.Accept(visitor)
	}
}

// AddElement 示意方法，组件对象结构，向对象结构中添加元素
// 不同的对象结构有不同的构建方式
func (obj *ObjectStruct) AddElement(ele Element) {
	obj.col = append(obj.col, ele)
}
